#include "fitting.h"
#include "ui_fitting.h"
#include <any>
#include <QSplineSeries>
#include <QAxObject>
#include "analyse.h"

Fitting::Fitting(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Fitting)
{
    ui->setupUi(this);
    connect(ui->back,&QPushButton::clicked,[=](){
        emit back();
    });
    setWindowTitle("拟合图");
    setWindowIcon(QIcon(":/image/P_mathematics.svg.png"));
    QStringList header={"x值","y值"};
    ui->lineEdit->setPlaceholderText("请输入标题");
    ui->order->setPlaceholderText("请输入整数阶");
    ui->xname->setPlaceholderText("请输入X轴名称");
    ui->yname->setPlaceholderText("请输入Y轴名称");
    ui->fitting->setChecked(1);
    ui->tableWidget->setColumnCount(2);
    ui->tableWidget->setRowCount(5);
    ui->tableWidget->setHorizontalHeaderLabels(header);//表头
    ui->tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
    ui->tableWidget->verticalHeader()->setVisible(1);
    ui->tableWidget->setEditTriggers(QAbstractItemView::CurrentChanged);//可编辑
    ui->tableWidget->setAlternatingRowColors(1);//深浅交替
}

Fitting::~Fitting()
{
    delete ui;
}

//该函数的作用是使用最小二乘法拟合一个最高次数为orders的多项式，使得该多项式与给定的数据点（X,Y）的拟合误差最小
//函数返回拟合多项式的系数向量
//orders为阶数
Eigen::VectorXf FitterLeastSquareMethod(std::vector<float> &X, std::vector<float> &Y, uint8_t orders)
{
    if (X.size() < 2 || Y.size() < 2 || X.size() != Y.size() || orders < 1)
        exit(EXIT_FAILURE);//退出并返回失败状态

    //将 X 和 Y 两个向量中的数据映射到了 Eigen::VectorXf 类型的对象 sampleX 和 sampleY 中，以便在 Eigen 中进行处理
    Eigen::Map<Eigen::VectorXf> sampleX(X.data(), X.size());
    Eigen::Map<Eigen::VectorXf> sampleY(Y.data(), Y.size());

    //orders+1 未知数个数
    Eigen::MatrixXf mtxVandermonde(X.size(), orders + 1);//行 列
    Eigen::VectorXf colVandermonde = sampleX;

    //循环来生成矩阵的每一列
    for (size_t i = 0; i < orders + 1; ++i)
    {
        if (i == 0)
        {
            mtxVandermonde.col(0) = Eigen::VectorXf::Constant(X.size(), 1, 1);
            continue;
        }
        if (i == 1)
        {
            mtxVandermonde.col(1) = colVandermonde;//第一列全为1
            continue;
        }
        colVandermonde = colVandermonde.array()*sampleX.array();
        mtxVandermonde.col(i) = colVandermonde;
    }

    //*矩阵乘法  .transpose()矩阵转置  .inverse()矩阵求逆
    Eigen::VectorXf result = (mtxVandermonde.transpose()*mtxVandermonde).inverse()*(mtxVandermonde.transpose())*sampleY;
    //result中保存了Vandermonde矩阵的最小二乘解，用于拟合数据集的多项式模型

    return result;
}

void Fitting::on_create_clicked()
{
    int rowcount=ui->tableWidget->rowCount();

    for(int i=0;i<rowcount;i++)
        for(int j=0;j<2;j++)
        {
            if(ui->tableWidget->item(i,j)==nullptr||ui->tableWidget->item(i,j)->text()==nullptr)
            {
                QMessageBox::warning(this,"警告","你还有数据没输入！");
                return;
            }
        }

    std::vector<float> X;
    std::vector<float> Y;
    for (int i = 0; i < rowcount; ++i)
    {
        X.push_back(ui->tableWidget->item(i, 0)->text().toFloat());
        Y.push_back(ui->tableWidget->item(i, 1)->text().toFloat());
    }

    double ymax=ui->tableWidget->item(0, 1)->text().toDouble();
    double ymin=ui->tableWidget->item(0, 1)->text().toDouble();
    QVector<QPointF>point;//储存点信息
    for(int i=0;i<rowcount;i++)
    {
        point.push_back(QPoint(ui->tableWidget->item(i,0)->text().toDouble(),ui->tableWidget->item(i,1)->text().toDouble()));
        ymax=ymax>ui->tableWidget->item(i,1)->text().toDouble()?ymax:ui->tableWidget->item(i,1)->text().toDouble();
        ymin=ymin<ui->tableWidget->item(i,1)->text().toDouble()?ymin:ui->tableWidget->item(i,1)->text().toDouble();
    }
    std::sort(point.begin(),point.end(),[](const QPointF& a, const QPointF& b) -> bool{
        return a.x()<b.x();//排序
    });

    QChart*chart=new QChart();
    chart->setAnimationOptions(QChart::SeriesAnimations);
    chart->setTitle(ui->lineEdit->text());
    chart->setTitleFont(QFont("微软雅黑"));

    QValueAxis *axisY = new QValueAxis;
    chart->addAxis(axisY, Qt::AlignLeft);
    axisY->setLineVisible(true);
    axisY->setTitleText(ui->yname->text());

    QValueAxis *axisX = new QValueAxis;
    chart->addAxis(axisX,Qt::AlignBottom);
    axisX->setLineVisible(true);
    axisX->setRange(point[0].x()-1,point[rowcount-1].x()+1);
    axisX->setTitleText(ui->xname->text());

    QScatterSeries*sery=new QScatterSeries();
    for(int i=0;i<rowcount;i++)
        sery->append(point[i]);
    chart->addSeries(sery);
    sery->attachAxis(axisX);
    sery->attachAxis(axisY);
    sery->setName("散点");
    chart->addSeries(sery);

    if(ui->fitting->isChecked())
    {
        if(ui->order->text()==nullptr)
        {
            QMessageBox::warning(this,"警告","你还未输入阶数！");
            return;
        }
        Eigen::VectorXf result=FitterLeastSquareMethod(X, Y, ui->order->text().toInt());

        std::string str;//储存拟合曲线函数表达式
        for (int i = result.size()-1; i >=0 ; i--)
        {
            if (i==0)
            {
                str += "+" + std::to_string(result[i]);
                continue;
            }
            else if (i==result.size()-1)
            {
                str += "y=" + std::to_string(result[i]) + "*x^"+std::to_string(i);
                continue;
            }
            else if(i==1)
            {
                str += "+" + std::to_string(result[i])+"*x";
                continue;
            }
            str += "+" + std::to_string(result[i]) + "*x^" + std::to_string(i);
        }
        //将str中的+-替换为-
        for (int i = 0; i < str.size(); ++i)
        {
            if (str[i] == '+' && str[i + 1] == '-')
            {
                str[i] = '-';
                str.erase(i + 1, 1);
            }
        }
        QSplineSeries *sery1=new QSplineSeries();
        std::vector<std::any> post;
        std::vector<std::pair<double,double>> mypoint;
        Analyse::getPostfix(str,post);//调用anlayse中的函数获得逆波兰表达式
        for(double x=point[0].x()-1;x<=point[rowcount-1].x()+1;x+=0.001)
        {
            double y=0;
            Analyse::getResult(post,y,'x',x);
            ymin=ymin<y?ymin:y;
            ymax=ymax>y?ymax:y;
            mypoint.push_back(std::make_pair(x,y));
        }
        for(auto& i:mypoint)
            sery1->append(i.first, i.second);
        chart->addSeries(sery1);
        sery1->attachAxis(axisX);
        sery1->attachAxis(axisY);
        sery1->setName(QString::fromStdString(str));
        qDebug() << QString::fromStdString(str);
    }
    axisY->setRange(ymin-1,ymax+1);
    QChartView *chartView = new QChartView(chart);
    chartView->setWindowIcon(QIcon(":/image/P_mathematics.svg.png"));
    chartView->setWindowTitle("拟合曲线");
    chartView->setRenderHint(QPainter::Antialiasing);
    chartView->resize(800, 600);
    chartView->show();
}

void Fitting::on_add_clicked()
{
    int rowcount=ui->tableWidget->rowCount();
    ui->tableWidget->insertRow(rowcount);
}

void Fitting::on_delete_2_clicked()
{
    int rowcount=ui->tableWidget->rowCount();
    if(rowcount>2)
        ui->tableWidget->setRowCount(rowcount-1);
    else
        QMessageBox::warning(this,"警告","至少保留两个点");
}

void Fitting::on_clear_clicked()
{
    int rowcount=ui->tableWidget->rowCount();
    int columncount=ui->tableWidget->columnCount();
    for(int i=0;i<rowcount;i++)
        for(int j=0;j<columncount;j++){
            QTableWidgetItem* item = ui->tableWidget->item(i, j);
            if(item==nullptr)
                ui->tableWidget->setItem(i, j, new QTableWidgetItem(""));
            else
                ui->tableWidget->item(i,j)->setText("");
        }
}

void Fitting::on_pushButton_clicked()
{
    QString fileName=QFileDialog::getOpenFileName(this,"读取","C:\\Users","(*.xlsx)");
    if(fileName.isEmpty())
        return;
    fileName=QDir::toNativeSeparators(fileName);

    QAxObject* excel = new QAxObject("Excel.Application", this);
    QAxObject* workbooks = excel->querySubObject("Workbooks");
    QAxObject* workbook = workbooks->querySubObject("Open(const QString&)", fileName);
    QAxObject* sheets = workbook->querySubObject("Sheets");
    QAxObject* sheet = sheets->querySubObject("Item(int)", 1); // 第一个工作表
    QAxObject* usedRange = sheet->querySubObject("UsedRange");
    QAxObject* rows = usedRange->querySubObject("Rows");

    int rowCount = rows->property("Count").toInt();

    ui->tableWidget->setRowCount(rowCount);

    for(int i=0;i<ui->tableWidget->rowCount();i++)
        for(int j=0;j<2;j++)
        {
            QTableWidgetItem* item = ui->tableWidget->item(i, j);
            if(item==nullptr)
                ui->tableWidget->setItem(i, j, new QTableWidgetItem(""));
        }

    for (int row = 1; row <= rowCount; ++row)
    {
        for (int column = 1; column <= 2; ++column)
        {
            QAxObject* cell = sheet->querySubObject("Cells(int,int)", row, column);
            QString value = cell->property("Value").toString();
            ui->tableWidget->item(row-1, column-1)->setText(value);
        }
    }

    workbook->dynamicCall("Close()");
    excel->dynamicCall("Quit()");
}
